/** @file    DeviceManager.cpp
 *  @author  Alessandro Polo
 *  @version $Id: DeviceManager.cpp 2832 2010-08-02 22:53:46Z alex $
 *  @brief
 * File containing methods for the wosh::DeviceManager class.
 * The header for this class can be found in DeviceManager.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <core/DeviceManager.h>

 #include <core/ObjectAllocator.h>
 #include <core/BusRing.h>
 #include <core/Thread.h>
 #include <core/MessageFilter.h>
 #include <core/BusManager.h>
 #include <core/SystemError.h>
 #include <core/WoshKernel.h>

 #include <core/MethodsCommon.h>
 #include <core/Fact.h>
 #include <core/List.h>
 #include <core/Table.h>
 #include <core/MessageProcessingCommon.h>
 #include <core/DataModels.h>

 #include <core/Bundle.h>

using namespace std;
namespace wosh {

 WOSH_REGISTER(wosh::Device, "wosh::Object", 0.01, _static_Device )

// Gamma singleton! static reference is allocated and maintained by WoshKernel
extern "C" {
	DeviceManager* DeviceManager::getInstance() { 
		if ( WoshKernel::getInstance() == NULL ) return NULL;
		return &WoshKernel::getInstance()->devices();
	}
}
 // register as abstract because it is a singleton:
 WOSH_REGISTER_INTERFACE(wosh::DeviceManager, "wosh::WoshModule", 0.01, _static_DeviceManager )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

DeviceManager::DeviceManager( const WoshKernel* parent )
	: WoshModule(parent, _DeviceManager_NAME, _DeviceManager_URI) {

//Log.setLevel( VERBOSE );

	Log(LOG_DEBUG, " Initializing [LogMaxSize=%ld; LogLocally=true]..", _DeviceManager_DEFAULT_EventLogMaxSize );
	this->eventLogMaxSize = _DeviceManager_DEFAULT_EventLogMaxSize;
	this->eventLogLocallyOnly = true;

	Log(LOG_DEBUG, " Initializing Properties.." );
	Properties.set( _DeviceManager_KEY_EventLogMaxSize,		this->eventLogMaxSize, Permission::RW_RW_R__() );
	Properties.set( _DeviceManager_KEY_EventLogLocallyOnly,	true, Permission::RW_RW_R__() );
	Properties.monitor(_DeviceManager_KEY_EventLogMaxSize);
	Properties.monitor(_DeviceManager_KEY_EventLogLocallyOnly);

	Log(LOG_DEBUG, " Registering Methods.." );
	MethodMessageResponse* mmListDevicesByType = Methods.create<MethodMessageResponse>( _DeviceManager_METHOD_GetListByType, "List Devices of Type", Permission(Permission::RX) );
	mmListDevicesByType->setMethod( this, (MethodMessageResponsePtr)&DeviceManager::mmDoListDevicesByType );

	MethodRequest* mmRegisterDevice = Methods.create<MethodRequest>( _DeviceManager_METHOD_register, "register Device", Permission::R_XR__R__() );
	mmRegisterDevice->setMethod( this, (MethodRequestPtr)&DeviceManager::mmDoRegisterDevice );
	MethodRequest* mmUnRegisterDevice = Methods.create<MethodRequest>( _DeviceManager_METHOD_unregister, "unregister Device", Permission::R_XR__R__() );
	mmUnRegisterDevice->setMethod( this, (MethodRequestPtr)&DeviceManager::mmDoUnRegisterDevice );
	MethodMessageResponse* mmFakeDeviceEvent = Methods.create<MethodMessageResponse>( _DeviceManager_METHOD_fake_event, "fake Device event", Permission::R_XR__R__() );
	mmFakeDeviceEvent->setMethod( this, (MethodMessageResponsePtr)&DeviceManager::mmDoFakeDeviceEvent );

	MethodRequest* mmEventsLog = Methods.create<MethodRequest>( _DeviceManager_METHOD_eventslog, "list Devices' events", Permission::R_XR__R__() );
	mmEventsLog->setMethod( this, (MethodRequestPtr)&DeviceManager::mmDoEventsLog );
	MethodWRESULT* mmClearLog = Methods.create<MethodWRESULT>( _DeviceManager_METHOD_clear_log, "clear events log", Permission::R_XR__R__() );
	mmClearLog->setMethod( this, (MethodWRESULTPtr)&DeviceManager::clearEventsLog );

	DataAdapterMapT<string, Device>* dataAdapter = new DataAdapterMapT<string, Device>(this->devices);
	DataFieldCollector<Device>* fields = new DataFieldCollector<Device>();
	fields->setDataSetName("Devices");
	fields->add<const URI&, URI>(&Device::getURI, "URI", 0);
	fields->add<const char*, Variant>(&Device::getClassName, "Type", 1);
	fields->add<const std::string&, Variant>(&Device::getBundleOwner, "BundleOwner", 2);
	fields->add<const Permission&,Permission>(&Device::getPermission, "Permission", 3);
	DataFieldSubWrapperT<Device, const InterfacesProvider&, List*, void>* dfwInterfacesList;
	dfwInterfacesList = new DataFieldSubWrapperT<Device, const InterfacesProvider&, List*, void>(&Device::getInterfaces, &InterfacesProvider::toList, "Interfaces", 4);
	fields->add(dfwInterfacesList);
	fields->add<const std::string&,Variant>(&Device::getEntityID, "EntityID", 5, false);
	DataFieldExtractor<Device>* dExt = new DataFieldExtractor<Device>(dataAdapter, fields);
	MethodList* mmListDevices = Methods.create<MethodList>( _METHOD_List, "Get Devices Table", Permission::R_XR_XR__() );
	mmListDevices->setDataFieldExtractor(dExt);

	MethodRetrieve* mmRetrieveDevices = Methods.create<MethodRetrieve>( _METHOD_Retrieve, "Retrieve Device(s)", Permission::R_XR_XR__() );
	mmRetrieveDevices->setItemExtractor( ObjectExtractor<Device, const std::string&>::createFor< DataAdapterMapT<string, Device> >(this->devices, &Device::getName ) );
 }

DeviceManager::~DeviceManager() {
	Log(LOG_DEBUG, ":~DeviceManager() Destroying.." );

	Log(LOG_VERBOSE, ":~DeviceManager() Freeing Devices [%d]..", this->devices.count() );
	this->devices.clear();

	Log(LOG_VERBOSE, ":~DeviceManager() Freeing EventsLog [%d]..", this->eventsLog.count() );
	this->eventsLog.clear();

	Log(LOG_VERBOSE, " Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS

WRESULT DeviceManager::onKernelInitializing() {
	WRESULT ret = WoshModule::onKernelInitializing();

	Log(LOG_VERBOSE, " Initializing BusRing ("_Bus_Devices").." );
	Bus* devicesBus = new BusRing(_Bus_Devices);
	devicesBus->setParentObject(this);
	devicesBus->setSharedLocal(true);
	devicesBus->setSharedNetwork(true);
	WRESULT bus_ok = WoshKernel::getInstance()->busses().registerBus(devicesBus);
	if ( WFAILED(bus_ok) )
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "onKernelInitializing", "FAILED#%d Creating Bus ("_Bus_Devices")..", bus_ok );
	else {
		Log(LOG_INFO, " Created BusRing ("_Bus_Devices")." );
		BusDevices.setMessageHandler(this);
		bus_ok = BusDevices.connect( _Bus_Devices );
		if ( WFAILED(bus_ok) )
			SystemError::raise(this, Log, SystemError::CRITICAL, 0, "onKernelInitializing", "FAILED#%d Connecting Bus ("_Bus_Devices")..", bus_ok );
		else
			Log(LOG_DEBUG, " Connected to "_Bus_Devices"." );
	 }

	// setup DSM
	this->dsmDevices.setName(_DeviceManager_DSM_NAME);
	//this->dsmDevices.setOwnerObject(this);
	//this->dsmDevices.setParentObject(this);
	//WRESULT dsm_init = this->dsmDevices.init();
	//if ( WFAILED(dsm_init) )
	//	SystemError::raise(this, Log, SystemError::CRITICAL, 0, "onKernelInitializing", "FAILED#%d Initializing DSM ("_DeviceManager_DSM_NAME")..", dsm_init );
	//else
	//	Log(LOG_INFO, " Initialized DSM ("_DeviceManager_DSM_NAME")" );

	return ret + bus_ok;
 }

WRESULT DeviceManager::onKernelStopping() {
	Log(LOG_VERBOSE, ":onKernelStopped() DeInitializing DSM.." );
	WRESULT ret = WRET_OK;//this->dsmDevices.deinit();
	if ( WFAILED(ret) )
		Log(LOG_CRITICAL, ":onKernelStopping() ERROR#%d DeInitializing DSM.", ret );
	ret += WoshModule::onKernelStopping();
	return ret;
 }

/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENTs

bool DeviceManager::updatingProperty( bool& do_update, const Variant& value_proposed, Property& property_current, const PropertiesProvider* source )
 {
	if ( property_current.getKey() == _DeviceManager_KEY_EventLogMaxSize ) {
		this->eventsLog.transactionBeginWrite();
		this->eventLogMaxSize = value_proposed.toLong(this->eventLogMaxSize);
		this->eventsLog.transactionEnd();
		return true;
	 }
	else if ( property_current.getKey() == _DeviceManager_KEY_EventLogLocallyOnly ) {
		this->eventsLog.transactionBeginWrite();
		this->eventLogLocallyOnly = value_proposed.toBoolean(this->eventLogLocallyOnly);
		this->eventsLog.transactionEnd();
		return true;
	 }
	// forward processing to base class
	return WoshModule::updatingProperty(do_update, value_proposed, property_current, source);
 }

///////////////////////////////////////////////////////////////////////////////////////////// PROPERTY EVENTs
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////// COLLECTOR

bool DeviceManager::exists( const string& deviceName ) const {
	MutexLockerRead mL(this->devices.getMutex());
	return this->devices.exists(deviceName);
 }

WRESULT DeviceManager::registerDevice( Device* device ) {
	MutexLockerWrite mL(this->devices.getMutex());
	return registerDevice_(device, true);
 }

WRESULT DeviceManager::registerDevice_( Device* device, bool raise_event ) {
	if ( device == NULL ) return WRET_ERR_PARAM;
	if ( device->getName().size() <= 3 ) {
		Log(LOG_CRITICAL, ":registerDevice() : FAILED Registering '%s' [Empty name!]", device->getName().c_str() );
		return WRET_ERR_PARAM;
	 }
	if ( device->getURI().isGlobal() ) device->getURI().unRegisterGlobal();
	device->getURI().setKernelLocal();
	device->getURI().setParent( this->getURI() );
	device->getURI().setName( device->getName() );
	device->getURI().registerGlobal();

	Device* oldObj = this->devices.find(device->getName());
	Fact* fact = NULL;
	if ( raise_event )
		fact = new DeviceManager_DeviceUpdated(device->getName(), (oldObj==NULL) );
	if ( oldObj == NULL ) {
		Log(LOG_INFO, ":registerDevice(%s) Registered as New", device->getName().c_str() );
		this->devices.set(device->getName(), device);
		// also add object to DSM dataset
		this->dsmDevices.doObjectAdd(device);
	 }
	else {
		Log(LOG_WARNING, ":registerDevice(%s) Device Exists, overwriting", device->getName().c_str() );
		*oldObj = *device;
		this->dsmDevices.doObjectUpdated(oldObj);
		delete device;
	 }
	Properties.transactionBeginWrite();
	Properties.update( _DeviceManager_KEY_Devices, this->devices.size() );
	Properties.transactionEnd();
	if ( fact != NULL ) {
		Message* msg_event = new Message(fact);
		msg_event->setDestinationBroadcast(true);
		signMessage(msg_event);
		BusCore.postMessage(msg_event);
	 }
	return WRET_OK;
 }

WRESULT DeviceManager::registerDevices( vector<Device*>& new_devices ) {
	if ( new_devices.empty() ) {
		Log(LOG_WARNING, ":registerDevices() : No device found" );
		return WRET_ERR_PARAM;
	 }
	WRESULT ret = WRET_OK;
//	List* dList = new List(new_devices.size());
	std::vector<Device*>::iterator it;
	for ( it=new_devices.begin(); it!=new_devices.end(); ++it ) {
		if ( *it == NULL ) continue;
		ret = registerDevice_( *it, false );
		if ( WFAILED(ret) ) continue;
//		dList->push_back<Variant>( (*it)->getName() );
	 }
//	if ( dList->isEmpty() ) {
//		delete dList;
//		return WRET_ERR_PARAM;
//	 }
//	Log(LOG_INFO, ":registerDevices() : %d device registered/updated (of %d)", dList->size(), new_devices.size() );
//	Message* msg_event = new Message( new Fact( _DeviceManager_EVENT_DeviceAdded, dList ) );
//	msg_event->setDestinationBroadcast(true);
//	signMessage(msg_event);
//	BusCore.postMessage(msg_event);
	return ret;
 }

WRESULT DeviceManager::unRegisterDevice( const std::string& devName ) {
	this->devices.transactionBeginWrite();
	if ( !this->devices.exists(devName) ) {
		this->devices.transactionEnd();
		Log(LOG_CRITICAL, ":unRegisterDevice() : FAILED UnRegistering %s [NOT Exists!]", devName.c_str() );
		return WRET_ERR_PARAM;
	 }
	Log(LOG_INFO, ":unRegisterDevice() : %s", devName.c_str() );

	WRESULT ret = this->devices.erase(devName, true);
	this->devices.transactionEnd();
	if ( WFAILED(ret) ) {
		Log(LOG_CRITICAL, ":unRegisterDevice(%s) : FAILED#%d UnRegistering ", devName.c_str(), ret );
		return ret;
	 }
	Message* msg_event = new Message( new DeviceManager_DeviceUpdated(devName, 2 ) );
	msg_event->setDestinationBroadcast(true);
	signMessage(msg_event);
	BusCore.postMessage(msg_event);
	return WRET_OK;
 }

WRESULT DeviceManager::unRegisterDevicesOf( const std::string& ownerName ) {
	Log(LOG_VERBOSE, ":unRegisterDevicesOf(%s) : Looping..", ownerName.c_str() );
	this->devices.transactionBeginWrite();
//	List* dList = new List();
	tStrDeviceMap::Iterator it;
	const tStrDeviceMap::ConstIterator it_end = this->devices.end();
	for ( it=this->devices.begin(); it!=it_end;) {
		if ( it->second == NULL ) { ++it; continue; }
//		dList->push_back<Variant>( it->first );
		delete it->second; it->second = NULL;
		this->devices.getMap().erase(it);
	 }
	this->devices.transactionEnd();
//	if ( dList->isEmpty() ) {
//		Log(LOG_WARNING, ":unRegisterDevicesOf(%s) : No devices found", ownerName.c_str() );
//		return WRET_ERR_PARAM;
//	 }
//	Log(LOG_INFO, ":unRegisterDevicesOf(%s) : %d devices removed", ownerName.c_str(), dList->size() );
//	Message* msg_event = new Message( new Fact( _DeviceManager_EVENT_DeviceRemoved, dList ) );
//	msg_event->setDestinationBroadcast(true);
//	signMessage(msg_event);
//	BusCore.postMessage(msg_event);
	return WRET_OK;
 }

/////////////////////////////////////////////////////////////////////////////////////////////////// COLLECTOR
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT DeviceManager::getInterfacesOf( const std::string& devName, InterfacesProvider* interfaces ) {
	MutexLockerRead mL(this->devices.getMutex());
	Device* device = this->devices.find(devName);
	if ( device == NULL ) {
		Log(LOG_WARNING, ":getInterfacesOf(%s) Device not found.", devName.c_str() );
		return WRET_ERR_PARAM;
	 }
	Log(LOG_VERBOSE, ":getInterfacesOf(%s) [%d]", devName.c_str(), device->getInterfaces().count() );
	*interfaces = device->getInterfaces(); // copy
	return WRET_OK;
 }

/////////////////////////////////////////////////////////////////////////////////////////////////// COLLECTOR
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// BUS EVENTS

void DeviceManager::busMessage( const Message& message, const Bus* source ) {
	if ( message.isEmpty() ) return;
	if ( message.getContent()->isFact() && message.getSourceBus() == _Bus_Devices ) {
		logEvent(message);
		return;
	 }
	if ( !message.getContent()->isRequest() ) return;
	// avoid message replica
	if ( !MessageFilter::filterReplica( message, &BusCore ) )
		return;
	WoshModule::busMessage( message, source );
 }

////////////////////////////////////////////////////////////////////////////////////////////////// BUS EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void DeviceManager::logEvent( const Message& message ) {
	if ( this->eventLogMaxSize == 0 ) return;
	if ( this->eventLogLocallyOnly  && !message.getSource().isKernelEmptyOrLocal() ) return;
	this->eventsLog.transactionBeginWrite();
	if ( this->eventsLog.size() >= this->eventLogMaxSize ) {
		this->eventsLog.erase( this->eventsLog.begin(), true );
	 }
	this->eventsLog.push_back( message.clone() );
	this->eventsLog.transactionEnd();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS

WRESULT DeviceManager::clearEventsLog() {
	Log(LOG_INFO, "clearEventsLog()" );
	this->eventsLog.clear();
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* DeviceManager::mmDoRegisterDevice( const Request* request ) {
	if ( request == NULL ) return NULL;
	if ( !request->hasData() )
		return request->replyResponse(WRET_ERR_PARAM, "missing argument");
	WRESULT ret = WRET_ERR_INTERNAL;
	if ( request->getData()->isKindOf<Device>() ) {
		const Device* device = request->getData()->as<Device>();
		if ( device == NULL )
			return request->replyResponse(WRET_ERR_PARAM, "invalid device argument");
		ret = registerDevice( device->clone() );
	 }
	if ( request->getData()->isKindOf<List>() ) {
		const List* devices = request->getData()->as<List>();
		if ( devices == NULL )
			return request->replyResponse(WRET_ERR_PARAM, "invalid device argument");
		List* devices_cp = devices->clone();
		vector<Device*> dv = devices_cp->getVector<Device>();
		ret = registerDevices( dv );
		devices_cp->getVector().clear();
		delete devices_cp;
	 }
	return request->replyResponse(ret);
 }

Response* DeviceManager::mmDoUnRegisterDevice( const Request* request ) {
	if ( request == NULL ) return NULL;
	Variant dDeviceName = request->getArgument();
	if ( !dDeviceName.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "invalid name");
	WRESULT ret = unRegisterDevice( dDeviceName.asString() );
	return request->replyResponse(ret);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* DeviceManager::mmDoFakeDeviceEvent( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	if ( request == NULL ) return NULL;
	Variant dDeviceName; Variant dEventName; Variant dEventData;
	request->extractArguments(dDeviceName, dEventName, dEventData);
	if ( !dDeviceName.isStringNotEmpty() || !dEventName.isStringNotEmpty() )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid arguments [device-name, event-name]");
	Log(LOG_INFO, "mmDoFakeDeviceEvent(%s) Fact: %s [%s]", dDeviceName.asString().c_str(), dEventName.asString().c_str(), dEventData.toString().c_str() );
	Message* msg_event = new Message( new Fact(dEventName.asString(), new Variant(dEventData)) );
	msg_event->setSource( this );
	msg_event->getSource().setParent(this->getURI());
	msg_event->getSource().setName( dDeviceName.asString() );
	msg_event->setDestinationBroadcast();
	SecurityManager::getCredentialProxy().signMessage(msg_event, requestMessage->getSecurityToken(), this);
	BusDevices.postMessage(msg_event);
	return request->replyResponse(WRET_OK, "Device-Event (message) sent!" );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* DeviceManager::mmDoListDevicesByType( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	string option;
	string device_type = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoListDevicesByType(%s)", option.c_str() );
	if ( option == "s" || option == "short" ) {
		this->devices.transactionBeginRead();
		List* objs = new List(this->devices.size());
		tStrDeviceMap::ConstIterator it;
		const tStrDeviceMap::ConstIterator it_end = this->devices.end();
		for ( it=this->devices.begin(); it!=it_end; ++it ) {
			if ( it->second == NULL ) continue;
			if ( !it->second->getInterfaces().existsClass(device_type) ) continue;
			objs->append( it->second->getURI().clone() );
		 }
		this->devices.transactionEnd();
		return request->replyResponse(WRET_OK, objs);
	 }
	else {
		this->devices.transactionBeginRead();
		Table* tbl = new Table( this->devices.size(), 5, true );
		tbl->setTableName("Devices");
		tbl->setHeaderHorizontal("URI", 0);
		tbl->setHeaderHorizontal("Type", 1);
		tbl->setHeaderHorizontal("BundleOwner", 2);
		tbl->setHeaderHorizontal("Perms", 3);
		tbl->setHeaderHorizontal("Interfaces", 4);
		unsigned int iRow = 0;
		tStrDeviceMap::ConstIterator it;
		const tStrDeviceMap::ConstIterator it_end = this->devices.end();
		for ( it=this->devices.begin(); it!=it_end; ++it, ++iRow ) {
			if ( it->second == NULL ) continue;
			if ( !it->second->getInterfaces().existsClass(device_type) ) continue;
			const Device* cDev = it->second;
			tbl->set( cDev->getURI().clone(), iRow, 0);
			tbl->set<Variant>( cDev->getClassName(), iRow, 1);
			tbl->set<Variant>( cDev->getBundleOwner(), iRow, 2);
			tbl->set( cDev->getPermission().clone(), iRow, 3);
			tbl->set( cDev->getInterfaces().toList(), iRow, 4);
		 }
		this->devices.transactionEnd();
		return request->replyResponse(WRET_OK, tbl);
	 }
 }

Response* DeviceManager::mmDoEventsLog( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoEventsLog(%s)", option.c_str() );
	if ( option == "s" || option == "short" ) {
		this->eventsLog.transactionBeginRead();
		List* objs = new List(this->eventsLog.size());
		tMessageList::ConstIterator it;
		const tMessageList::ConstIterator it_end = this->eventsLog.end();
		for ( it=this->eventsLog.begin(); it!=it_end; ++it ) {
			if ( *it == NULL ) continue;
			if ( (*it)->isEmpty() ) continue;
			if ( !(*it)->getContent()->isFact() ) continue;
			objs->push_back<Variant>( (*it)->getContent()->asFact()->getEvent() );
		 }
		this->eventsLog.transactionEnd();
		return request->replyResponse(WRET_OK, objs);
	 }
	else {
		this->eventsLog.transactionBeginRead();
		Table* tbl = new Table( this->eventsLog.size(), 4, true );
		tbl->setTableName("EventLog");
		tbl->setHeaderHorizontal("TimeStamp", 0);
		tbl->setHeaderHorizontal("URI", 1);
		tbl->setHeaderHorizontal("Event", 2);
		tbl->setHeaderHorizontal("Data", 3);
		unsigned int iRow = 0;
		tMessageList::ConstIterator it;
		const tMessageList::ConstIterator it_end = this->eventsLog.end();
		for ( it=this->eventsLog.begin(); it!=it_end; ++it, ++iRow ) {
			if ( *it == NULL ) continue;
			if ( (*it)->isEmpty() ) continue;
			const Fact* fact = (*it)->getContent()->asFact();
			tbl->set( new Variant((*it)->getTimestamp(), Variant::DATETIME), iRow, 0);
			tbl->set( (*it)->getSource().clone(), iRow, 1);
			tbl->set<Variant>( fact->getEvent(), iRow, 2);
			if ( fact->hasData() )
				tbl->set( fact->getData()->clone(), iRow, 3);
			else
				tbl->set<Variant>( "NULL", iRow, 3);
		 }
		this->eventsLog.transactionEnd();
		return request->replyResponse(WRET_OK, tbl);
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void DeviceManager::busMessageForBundleDevices( const Message& message, const Bus*, Bundle* bundle, MethodsProvider& Methods, LogContext& Log, BusConnector& BusCore, BusConnector& BusDevices )
 {
	if ( message.isEmpty() || !message.getContent()->isRequest() ) return;
	if ( message.getSourceUUID() == bundle->getEntityID() ) return; // avoid loopback

	// as Device, we listen on two busses (Core and Devices) with different URI
	// a custom filtering implementation is required:

	// avoid message replicas
	if ( !MessageFilter::filterReplica( message, &BusCore ) )
		return;

	if ( !URI::pregMatchKernel( message.getDestination(), bundle->getURI() ) )
		return;

	if ( message.getSourceBus() == BusDevices.getBusName() ) {
		// my URI in wosh.Bus.Devices is JUST MY NAME!
		if ( !message.getDestination().getPath().empty() ) {
			if ( !URI::pregMatchStep( message.getDestination().getPath().at(0), "Devices" ) )
				return;
		 }
	 }

	if ( message.getSourceBus() == BusCore.getBusName() ) {
		// my URIS in wosh.Bus.Core are Devices/<NAME> and Bundles/<NAME>
		if ( message.getDestination().getPath().size() != 1 )
			return;
		if ( !URI::pregMatchStep( message.getDestination().getPath().at(0), "Bundles" ) &&
			!URI::pregMatchStep( message.getDestination().getPath().at(0), "Devices" ) )
			return;
	 }

	if ( !URI::pregMatchStep(message.getDestination().getName(), bundle->getURI().getName() ) )
		return;

	if ( message.getDestination().hasExpression() ) {
		bool valid = MessageFilter::validateExpression( message.getDestination().getExpression(), bundle->bundleProperties(), bundle->bundleInterfaces(), message.getSecurityToken() );
		if ( !valid )
			return;
	 }

	Message* reply = MPC_busMessageRequest( message, Methods, Log );
	if ( reply == NULL )
		return;
	reply->setSource( bundle );

	if ( message.getSourceBus() == BusDevices.getBusName() ) {
		reply->getSource().setPath( "Devices" ); // override source URI cos we are injecting in Device bus!
		reply->getSource().setName( bundle->getName() );
		BusDevices.postMessage(reply);
	 }
	else { //if ( message.getSourceBus() == BusCore.getBusName() ) {
		BusCore.postMessage(reply);
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::DeviceManager_DeviceUpdated, "wosh::Fact", 1.0, _static_DeviceManager_DeviceUpdated )

DeviceManager_DeviceUpdated::DeviceManager_DeviceUpdated( const std::string& devicename )
	: Fact( _DeviceManager_EVENT_DeviceUpdated ) {
	setData( new Variant( devicename ) );
 }

DeviceManager_DeviceUpdated::DeviceManager_DeviceUpdated( const std::string& devicename, short device_action )
	: Fact( _DeviceManager_EVENT_DeviceUpdated ) {
	List* args = List::createFrom<Variant>( devicename, (int)device_action );
	setData(args);
 }

std::string DeviceManager_DeviceUpdated::getDeviceName() const {
	const Variant* var = getDataItem<Variant>(0);
	if ( var == NULL ) return "";
	if ( var->isString() ) return var->asString();
	return var->toString();
 }

short DeviceManager_DeviceUpdated::getDeviceAction() const {
	const Variant* var = getDataItem<Variant>(1);
	if ( var == NULL ) return 1;
	if ( var->isNumeric() ) return 1;
	return var->toInteger(1);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
